<!DOCTYPE html>
<html lang="zh-cn">
<head>
  <title>Router 路由 - 为企业级框架和应用而生</title>
  <meta charset="utf-8">
  <meta name="description" content="index.description">
  <meta http-equiv="X-UA-Compatible" content="IE=Edge,chrome=1">
  <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
  <link rel="icon" href="/images/favicon.png" type="image/x-icon">
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/docsearch.js/2/docsearch.min.css" />
<link rel="stylesheet" href="/css/index.css">

    <script>
    !function(t,e,a,r,c){t.TracertCmdCache=t.TracertCmdCache||[],t[c]=window[c]||
      {_isInit:!0,call:function(){t.TracertCmdCache.push(arguments)},
      start:function(t){this.call('start',t)}},t[c].l=new Date;
      var n=e.createElement(a),s=e.getElementsByTagName(a)[0];
      n.async=!0,n.src=r,s.parentNode.insertBefore(n,s)}
    (window,document,'script','https://tracert.alipay.com/tracert.js','Tracert');
      Tracert.start({
        plugins: [ 'BucName' ],
        spmAPos: 'a454',
        spmBPos: 'b4893',
      });
    </script>
  
</head>
<body>
  <div class="nav" >
  <header>
    <a href="/zh-cn/" class="nav-logo leftpadding" alt="egg"><img src="https://zos.alipayobjects.com/rmsportal/VTcUYAaoKqXyHJbLAPyF.svg"></a>
    <ul class="nav-item">
      <li>
        <form id="search-form">
          <input type="text" id="search-query" class="search-query st-default-search-input">
        </form>
      </li>
      <li><a href="/zh-cn/intro/" alt="指南">指南</a></li><li><a href="/api/" alt="API">API</a></li><li><a href="/zh-cn/tutorials/index.html" alt="教程">教程</a></li><li><a href="https://github.com/search?q=topic%3Aegg-plugin&type=Repositories" alt="插件">插件</a></li><li><a href="https://github.com/eggjs/egg/releases" alt="发布日志">发布日志</a></li>
      
      
        <li class="translations">
          <a class="nav-link">Translations</a>
          <span class="arrow"></span><ul id="dropdownContent" class="dropdown-content"><li><a id="en" href="/en/basics/router.html" >English</a></li><li><a id="zh-cn" href="/zh-cn/basics/router.html" style="color: #22ab28">中文</a></li></ul>
        </li>
      
      <li><iframe src="https://ghbtns.com/github-btn.html?user=eggjs&repo=egg&type=star&count=true" frameborder="0" scrolling="0" width="150px" height="20px"></iframe></li>
    </ul>
    <a id="mobileTrigger" href="#" class="mobile-trigger">
      <ul>
        <li></li>
        <li></li>
        <li></li>
      </ul>
    </a>
  </header>
</div>
  <div id="container" class="container">
    <div class="page-main">
  <article class="markdown-body">
    <h1>Router 路由</h1>
    <p>Router 主要用来描述请求 URL 和具体承担执行动作的 Controller 的对应关系，
框架约定了 <code>app/router.js</code> 文件用于统一所有路由规则。</p>
<p>通过统一的配置，我们可以避免路由规则逻辑散落在多个地方，从而出现未知的冲突，集中在一起我们可以更方便的来查看全局的路由规则。</p>
<h2 id="如何定义-router"><a class="markdown-anchor" href="#如何定义-router">#</a> 如何定义 Router</h2>
<ul>
<li><code>app/router.js</code> 里面定义 URL 路由规则</li>
</ul>
<figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="comment">// app/router.js</span></span><br><span class="line"><span class="built_in">module</span>.exports = <span class="function"><span class="params">app</span> =&gt;</span> &#123;</span><br><span class="line">  <span class="keyword">const</span> &#123; router, controller &#125; = app;</span><br><span class="line">  router.get(<span class="string">'/user/:id'</span>, controller.user.info);</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure>
<ul>
<li><code>app/controller</code> 目录下面实现 Controller</li>
</ul>
<figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="comment">// app/controller/user.js</span></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">UserController</span> <span class="keyword">extends</span> <span class="title">Controller</span> </span>&#123;</span><br><span class="line">  <span class="keyword">async</span> info() &#123;</span><br><span class="line">    <span class="keyword">const</span> &#123; ctx &#125; = <span class="keyword">this</span>;</span><br><span class="line">    ctx.body = &#123;</span><br><span class="line">      name: <span class="string">`hello <span class="subst">$&#123;ctx.params.id&#125;</span>`</span>,</span><br><span class="line">    &#125;;</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>这样就完成了一个最简单的 Router 定义，当用户执行 <code>GET /user/123</code>，<code>user.js</code> 这个里面的 info 方法就会执行。</p>
<h2 id="router-详细定义说明"><a class="markdown-anchor" href="#router-详细定义说明">#</a> Router 详细定义说明</h2>
<p>下面是路由的完整定义，参数可以根据场景的不同，自由选择：</p>
<figure class="highlight js"><table><tr><td class="code"><pre><span class="line">router.verb(<span class="string">'path-match'</span>, app.controller.action);</span><br><span class="line">router.verb(<span class="string">'router-name'</span>, <span class="string">'path-match'</span>, app.controller.action);</span><br><span class="line">router.verb(<span class="string">'path-match'</span>, middleware1, ..., middlewareN, app.controller.action);</span><br><span class="line">router.verb(<span class="string">'router-name'</span>, <span class="string">'path-match'</span>, middleware1, ..., middlewareN, app.controller.action);</span><br></pre></td></tr></table></figure>
<p>路由完整定义主要包括5个主要部分:</p>
<ul>
<li>verb - 用户触发动作，支持 get，post 等所有 HTTP 方法，后面会通过示例详细说明。
<ul>
<li>router.head - HEAD</li>
<li>router.options - OPTIONS</li>
<li>router.get - GET</li>
<li>router.put - PUT</li>
<li>router.post - POST</li>
<li>router.patch - PATCH</li>
<li>router.delete - DELETE</li>
<li>router.del - 由于 delete 是一个保留字，所以提供了一个 delete 方法的别名。</li>
<li>router.redirect - 可以对 URL 进行重定向处理，比如我们最经常使用的可以把用户访问的根目录路由到某个主页。</li>
</ul>
</li>
<li>router-name 给路由设定一个别名，可以通过 Helper 提供的辅助函数 <code>pathFor</code> 和 <code>urlFor</code> 来生成 URL。(可选)</li>
<li>path-match - 路由 URL 路径。</li>
<li>middleware1 - 在 Router 里面可以配置多个 Middleware。(可选)</li>
<li>controller - 指定路由映射到的具体的 controller 上，controller 可以有两种写法：
<ul>
<li><code>app.controller.user.fetch</code> - 直接指定一个具体的 controller</li>
<li><code>'user.fetch'</code> - 可以简写为字符串形式</li>
</ul>
</li>
</ul>
<h3 id="注意事项"><a class="markdown-anchor" href="#注意事项">#</a> 注意事项</h3>
<ul>
<li>在 Router 定义中， 可以支持多个 Middleware 串联执行</li>
<li>Controller 必须定义在 <code>app/controller</code> 目录中。</li>
<li>一个文件里面也可以包含多个 Controller 定义，在定义路由的时候，可以通过 <code>${fileName}.${functionName}</code> 的方式指定对应的 Controller。</li>
<li>Controller 支持子目录，在定义路由的时候，可以通过 <code>${directoryName}.${fileName}.${functionName}</code> 的方式制定对应的 Controller。</li>
</ul>
<p>下面是一些路由定义的方式：</p>
<figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="comment">// app/router.js</span></span><br><span class="line"><span class="built_in">module</span>.exports = <span class="function"><span class="params">app</span> =&gt;</span> &#123;</span><br><span class="line">  <span class="keyword">const</span> &#123; router, controller &#125; = app;</span><br><span class="line">  router.get(<span class="string">'/home'</span>, controller.home);</span><br><span class="line">  router.get(<span class="string">'/user/:id'</span>, controller.user.page);</span><br><span class="line">  router.post(<span class="string">'/admin'</span>, isAdmin, controller.admin);</span><br><span class="line">  router.post(<span class="string">'/user'</span>, isLoginUser, hasAdminPermission, controller.user.create);</span><br><span class="line">  router.post(<span class="string">'/api/v1/comments'</span>, controller.v1.comments.create); <span class="comment">// app/controller/v1/comments.js</span></span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure>
<h3 id="restful-风格的-url-定义"><a class="markdown-anchor" href="#restful-风格的-url-定义">#</a> RESTful 风格的 URL 定义</h3>
<p>如果想通过 RESTful 的方式来定义路由，
我们提供了 <code>app.resources('routerName', 'pathMatch', controller)</code> 快速在一个路径上生成 <a href="https://en.wikipedia.org/wiki/Create,_read,_update_and_delete" target="_blank" rel="noopener">CRUD</a> 路由结构。</p>
<figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="comment">// app/router.js</span></span><br><span class="line"><span class="built_in">module</span>.exports = <span class="function"><span class="params">app</span> =&gt;</span> &#123;</span><br><span class="line">  <span class="keyword">const</span> &#123; router, controller &#125; = app;</span><br><span class="line">  router.resources(<span class="string">'posts'</span>, <span class="string">'/api/posts'</span>, controller.posts);</span><br><span class="line">  router.resources(<span class="string">'users'</span>, <span class="string">'/api/v1/users'</span>, controller.v1.users); <span class="comment">// app/controller/v1/users.js</span></span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure>
<p>上面代码就在 <code>/posts</code> 路径上部署了一组 CRUD 路径结构，对应的 Controller 为 <code>app/controller/posts.js</code> 接下来，
你只需要在 <code>posts.js</code> 里面实现对应的函数就可以了。</p>
<table>
<thead>
<tr>
<th>Method</th>
<th>Path</th>
<th>Route Name</th>
<th>Controller.Action</th>
</tr>
</thead>
<tbody>
<tr>
<td>GET</td>
<td>/posts</td>
<td>posts</td>
<td>app.controllers.posts.index</td>
</tr>
<tr>
<td>GET</td>
<td>/posts/new</td>
<td>new_post</td>
<td>app.controllers.posts.new</td>
</tr>
<tr>
<td>GET</td>
<td>/posts/:id</td>
<td>post</td>
<td>app.controllers.posts.show</td>
</tr>
<tr>
<td>GET</td>
<td>/posts/:id/edit</td>
<td>edit_post</td>
<td>app.controllers.posts.edit</td>
</tr>
<tr>
<td>POST</td>
<td>/posts</td>
<td>posts</td>
<td>app.controllers.posts.create</td>
</tr>
<tr>
<td>PUT</td>
<td>/posts/:id</td>
<td>post</td>
<td>app.controllers.posts.update</td>
</tr>
<tr>
<td>DELETE</td>
<td>/posts/:id</td>
<td>post</td>
<td>app.controllers.posts.destroy</td>
</tr>
</tbody>
</table>
<figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="comment">// app/controller/posts.js</span></span><br><span class="line">exports.index = <span class="keyword">async</span> () =&gt; &#123;&#125;;</span><br><span class="line"></span><br><span class="line">exports.new = <span class="keyword">async</span> () =&gt; &#123;&#125;;</span><br><span class="line"></span><br><span class="line">exports.create = <span class="keyword">async</span> () =&gt; &#123;&#125;;</span><br><span class="line"></span><br><span class="line">exports.show = <span class="keyword">async</span> () =&gt; &#123;&#125;;</span><br><span class="line"></span><br><span class="line">exports.edit = <span class="keyword">async</span> () =&gt; &#123;&#125;;</span><br><span class="line"></span><br><span class="line">exports.update = <span class="keyword">async</span> () =&gt; &#123;&#125;;</span><br><span class="line"></span><br><span class="line">exports.destroy = <span class="keyword">async</span> () =&gt; &#123;&#125;;</span><br></pre></td></tr></table></figure>
<p>如果我们不需要其中的某几个方法，可以不用在 <code>posts.js</code> 里面实现，这样对应 URL 路径也不会注册到 Router。</p>
<h2 id="router-实战"><a class="markdown-anchor" href="#router-实战">#</a> router 实战</h2>
<p>下面通过更多实际的例子，来说明 router 的用法。</p>
<h3 id="参数获取"><a class="markdown-anchor" href="#参数获取">#</a> 参数获取</h3>
<h4 id="query-string-方式"><a class="markdown-anchor" href="#query-string-方式">#</a> Query String 方式</h4>
<figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="comment">// app/router.js</span></span><br><span class="line"><span class="built_in">module</span>.exports = <span class="function"><span class="params">app</span> =&gt;</span> &#123;</span><br><span class="line">  app.router.get(<span class="string">'/search'</span>, app.controller.search.index);</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="comment">// app/controller/search.js</span></span><br><span class="line">exports.index = <span class="keyword">async</span> ctx =&gt; &#123;</span><br><span class="line">  ctx.body = <span class="string">`search: <span class="subst">$&#123;ctx.query.name&#125;</span>`</span>;</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="comment">// curl http://127.0.0.1:7001/search?name=egg</span></span><br></pre></td></tr></table></figure>
<h4 id="参数命名方式"><a class="markdown-anchor" href="#参数命名方式">#</a> 参数命名方式</h4>
<figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="comment">// app/router.js</span></span><br><span class="line"><span class="built_in">module</span>.exports = <span class="function"><span class="params">app</span> =&gt;</span> &#123;</span><br><span class="line">  app.router.get(<span class="string">'/user/:id/:name'</span>, app.controller.user.info);</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="comment">// app/controller/user.js</span></span><br><span class="line">exports.info = <span class="keyword">async</span> ctx =&gt; &#123;</span><br><span class="line">  ctx.body = <span class="string">`user: <span class="subst">$&#123;ctx.params.id&#125;</span>, <span class="subst">$&#123;ctx.params.name&#125;</span>`</span>;</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="comment">// curl http://127.0.0.1:7001/user/123/xiaoming</span></span><br></pre></td></tr></table></figure>
<h4 id="复杂参数的获取"><a class="markdown-anchor" href="#复杂参数的获取">#</a> 复杂参数的获取</h4>
<p>路由里面也支持定义正则，可以更加灵活的获取参数：</p>
<figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="comment">// app/router.js</span></span><br><span class="line"><span class="built_in">module</span>.exports = <span class="function"><span class="params">app</span> =&gt;</span> &#123;</span><br><span class="line">  app.router.get(<span class="regexp">/^\/package\/([\w-.]+\/[\w-.]+)$/</span>, app.controller.package.detail);</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="comment">// app/controller/package.js</span></span><br><span class="line">exports.detail = <span class="keyword">async</span> ctx =&gt; &#123;</span><br><span class="line">  <span class="comment">// 如果请求 URL 被正则匹配， 可以按照捕获分组的顺序，从 ctx.params 中获取。</span></span><br><span class="line">  <span class="comment">// 按照下面的用户请求，`ctx.params[0]` 的 内容就是 `egg/1.0.0`</span></span><br><span class="line">  ctx.body = <span class="string">`package:<span class="subst">$&#123;ctx.params[<span class="number">0</span>]&#125;</span>`</span>;</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="comment">// curl http://127.0.0.1:7001/package/egg/1.0.0</span></span><br></pre></td></tr></table></figure>
<h3 id="表单内容的获取"><a class="markdown-anchor" href="#表单内容的获取">#</a> 表单内容的获取</h3>
<figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="comment">// app/router.js</span></span><br><span class="line"><span class="built_in">module</span>.exports = <span class="function"><span class="params">app</span> =&gt;</span> &#123;</span><br><span class="line">  app.router.post(<span class="string">'/form'</span>, app.controller.form.post);</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="comment">// app/controller/form.js</span></span><br><span class="line">exports.post = <span class="keyword">async</span> ctx =&gt; &#123;</span><br><span class="line">  ctx.body = <span class="string">`body: <span class="subst">$&#123;<span class="built_in">JSON</span>.stringify(ctx.request.body)&#125;</span>`</span>;</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 模拟发起 post 请求。</span></span><br><span class="line"><span class="comment">// curl -X POST http://127.0.0.1:7001/form --data '&#123;"name":"controller"&#125;' --header 'Content-Type:application/json'</span></span><br></pre></td></tr></table></figure>
<blockquote>
<p>附：</p>
</blockquote>
<blockquote>
<p>这里直接发起 POST 请求会<strong>报错</strong>：'secret is missing'。错误信息来自 <a href="https://github.com/koajs/csrf/blob/2.5.0/index.js#L69" target="_blank" rel="noopener">koa-csrf/index.js#L69</a> 。</p>
</blockquote>
<blockquote>
<p><strong>原因</strong>：框架内部针对表单 POST 请求均会验证 CSRF 的值，因此我们在表单提交时，请带上 CSRF key 进行提交，可参考<a href="https://eggjs.org/zh-cn/core/security.html#%E5%AE%89%E5%85%A8%E5%A8%81%E8%83%81csrf%E7%9A%84%E9%98%B2%E8%8C%83">安全威胁csrf的防范</a></p>
</blockquote>
<blockquote>
<p><strong>注意</strong>：上面的校验是因为框架中内置了安全插件 <a href="https://github.com/eggjs/egg-security" target="_blank" rel="noopener">egg-security</a>，提供了一些默认的安全实践，并且框架的安全插件是默认开启的，如果需要关闭其中一些安全防范，直接设置该项的 enable 属性为 false 即可。</p>
</blockquote>
<blockquote>
<p>「除非清楚的确认后果，否则不建议擅自关闭安全插件提供的功能。」</p>
</blockquote>
<blockquote>
<p>这里在写例子的话可临时在 <code>config/config.default.js</code> 中设置</p>
</blockquote>
<figure class="highlight plain"><table><tr><td class="code"><pre><span class="line">exports.security = &#123;</span><br><span class="line">  csrf: false</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure>
<h3 id="表单校验"><a class="markdown-anchor" href="#表单校验">#</a> 表单校验</h3>
<figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="comment">// app/router.js</span></span><br><span class="line"><span class="built_in">module</span>.exports = <span class="function"><span class="params">app</span> =&gt;</span> &#123;</span><br><span class="line">  app.router.post(<span class="string">'/user'</span>, app.controller.user);</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="comment">// app/controller/user.js</span></span><br><span class="line"><span class="keyword">const</span> createRule = &#123;</span><br><span class="line">  username: &#123;</span><br><span class="line">    type: <span class="string">'email'</span>,</span><br><span class="line">  &#125;,</span><br><span class="line">  password: &#123;</span><br><span class="line">    type: <span class="string">'password'</span>,</span><br><span class="line">    compare: <span class="string">'re-password'</span>,</span><br><span class="line">  &#125;,</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line">exports.create = <span class="keyword">async</span> ctx =&gt; &#123;</span><br><span class="line">  <span class="comment">// 如果校验报错，会抛出异常</span></span><br><span class="line">  ctx.validate(createRule);</span><br><span class="line">  ctx.body = ctx.request.body;</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="comment">// curl -X POST http://127.0.0.1:7001/user --data 'username=abc@abc.com&amp;password=111111&amp;re-password=111111'</span></span><br></pre></td></tr></table></figure>
<h3 id="重定向"><a class="markdown-anchor" href="#重定向">#</a> 重定向</h3>
<h4 id="内部重定向"><a class="markdown-anchor" href="#内部重定向">#</a> 内部重定向</h4>
<figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="comment">// app/router.js</span></span><br><span class="line"><span class="built_in">module</span>.exports = <span class="function"><span class="params">app</span> =&gt;</span> &#123;</span><br><span class="line">  app.router.get(<span class="string">'index'</span>, <span class="string">'/home/index'</span>, app.controller.home.index);</span><br><span class="line">  app.router.redirect(<span class="string">'/'</span>, <span class="string">'/home/index'</span>, <span class="number">302</span>);</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="comment">// app/controller/home.js</span></span><br><span class="line">exports.index = <span class="keyword">async</span> ctx =&gt; &#123;</span><br><span class="line">  ctx.body = <span class="string">'hello controller'</span>;</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="comment">// curl -L http://localhost:7001</span></span><br></pre></td></tr></table></figure>
<h4 id="外部重定向"><a class="markdown-anchor" href="#外部重定向">#</a> 外部重定向</h4>
<figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="comment">// app/router.js</span></span><br><span class="line"><span class="built_in">module</span>.exports = <span class="function"><span class="params">app</span> =&gt;</span> &#123;</span><br><span class="line">  app.router.get(<span class="string">'/search'</span>, app.controller.search.index);</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="comment">// app/controller/search.js</span></span><br><span class="line">exports.index = <span class="keyword">async</span> ctx =&gt; &#123;</span><br><span class="line">  <span class="keyword">const</span> type = ctx.query.type;</span><br><span class="line">  <span class="keyword">const</span> q = ctx.query.q || <span class="string">'nodejs'</span>;</span><br><span class="line"></span><br><span class="line">  <span class="keyword">if</span> (type === <span class="string">'bing'</span>) &#123;</span><br><span class="line">    ctx.redirect(<span class="string">`http://cn.bing.com/search?q=<span class="subst">$&#123;q&#125;</span>`</span>);</span><br><span class="line">  &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">    ctx.redirect(<span class="string">`https://www.google.co.kr/search?q=<span class="subst">$&#123;q&#125;</span>`</span>);</span><br><span class="line">  &#125;</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="comment">// curl http://localhost:7001/search?type=bing&amp;q=node.js</span></span><br><span class="line"><span class="comment">// curl http://localhost:7001/search?q=node.js</span></span><br></pre></td></tr></table></figure>
<h3 id="中间件的使用"><a class="markdown-anchor" href="#中间件的使用">#</a> 中间件的使用</h3>
<p>如果我们想把用户某一类请求的参数都大写，可以通过中间件来实现。
这里我们只是简单说明下如何使用中间件，更多请查看 <a href="./middleware.html">中间件</a>。</p>
<figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="comment">// app/controller/search.js</span></span><br><span class="line">exports.index = <span class="keyword">async</span> ctx =&gt; &#123;</span><br><span class="line">  ctx.body = <span class="string">`search: <span class="subst">$&#123;ctx.query.name&#125;</span>`</span>;</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="comment">// app/middleware/uppercase.js</span></span><br><span class="line"><span class="built_in">module</span>.exports = <span class="function"><span class="params">()</span> =&gt;</span> &#123;</span><br><span class="line">  <span class="keyword">return</span> <span class="keyword">async</span> <span class="function"><span class="keyword">function</span> <span class="title">uppercase</span>(<span class="params">ctx, next</span>) </span>&#123;</span><br><span class="line">    ctx.query.name = ctx.query.name &amp;&amp; ctx.query.name.toUpperCase();</span><br><span class="line">    <span class="keyword">await</span> next();</span><br><span class="line">  &#125;;</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="comment">// app/router.js</span></span><br><span class="line"><span class="built_in">module</span>.exports = <span class="function"><span class="params">app</span> =&gt;</span> &#123;</span><br><span class="line">  app.router.get(<span class="string">'s'</span>, <span class="string">'/search'</span>, app.middlewares.uppercase(), app.controller.search)</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="comment">// curl http://localhost:7001/search?name=egg</span></span><br></pre></td></tr></table></figure>
<h3 id="太多路由映射"><a class="markdown-anchor" href="#太多路由映射">#</a> 太多路由映射?</h3>
<p>如上所述，我们并不建议把路由规则逻辑散落在多个地方，会给排查问题带来困扰。</p>
<p>若确实有需求，可以如下拆分：</p>
<figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="comment">// app/router.js</span></span><br><span class="line"><span class="built_in">module</span>.exports = <span class="function"><span class="params">app</span> =&gt;</span> &#123;</span><br><span class="line">  <span class="built_in">require</span>(<span class="string">'./router/news'</span>)(app);</span><br><span class="line">  <span class="built_in">require</span>(<span class="string">'./router/admin'</span>)(app);</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="comment">// app/router/news.js</span></span><br><span class="line"><span class="built_in">module</span>.exports = <span class="function"><span class="params">app</span> =&gt;</span> &#123;</span><br><span class="line">  app.router.get(<span class="string">'/news/list'</span>, app.controller.news.list);</span><br><span class="line">  app.router.get(<span class="string">'/news/detail'</span>, app.controller.news.detail);</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="comment">// app/router/admin.js</span></span><br><span class="line"><span class="built_in">module</span>.exports = <span class="function"><span class="params">app</span> =&gt;</span> &#123;</span><br><span class="line">  app.router.get(<span class="string">'/admin/user'</span>, app.controller.admin.user);</span><br><span class="line">  app.router.get(<span class="string">'/admin/log'</span>, app.controller.admin.log);</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure>
<p>也可直接使用 <a href="https://github.com/eggjs/egg-router-plus" target="_blank" rel="noopener">egg-router-plus</a>。</p>

  </article>
  <aside id="mobileAside" class="toc">
  <div class="mobile-menu">
    <ul>
      <li><a href="/zh-cn/intro/" alt="指南">指南</a></li><li><a href="/api/" alt="API">API</a></li><li><a href="/zh-cn/tutorials/index.html" alt="教程">教程</a></li><li><a href="https://github.com/search?q=topic%3Aegg-plugin&type=Repositories" alt="插件">插件</a></li><li><a href="https://github.com/eggjs/egg/releases" alt="发布日志">发布日志</a></li>
      
      
        <li class="translations">
          <a class="nav-link">Translations</a>
          <span class="arrow"></span><ul id="dropdownContent" class="dropdown-content"><li><a id="en" href="/en/basics/router.html" >English</a></li><li><a id="zh-cn" href="/zh-cn/basics/router.html" style="color: #22ab28">中文</a></li></ul>
        </li>
      
    </ul>
  </div>
  <dl><dt id="title-Intro" style="cursor: pointer;" class="aside-title">新手指南<a id="collapse-icon-Intro" class="icon opend"></a></dt><dd id=panel-Intro><ul><li><a href="/zh-cn/intro/index.html" class="menu-link">Egg.js 是什么?</a></li><li><a href="/zh-cn/intro/egg-and-koa.html" class="menu-link">Egg.js 和 Koa</a></li><li><a href="/zh-cn/intro/quickstart.html" class="menu-link">快速入门</a></li><li><a href="/zh-cn/tutorials/progressive.html" class="menu-link">渐进式开发</a></li><li><a href="/zh-cn/migration.html" class="menu-link">2.x 升级指南</a></li></ul></dd><dt id="title-Basics" style="cursor: pointer;" class="aside-title">基础功能<a id="collapse-icon-Basics" class="icon opend"></a></dt><dd id=panel-Basics><ul><li><a href="/zh-cn/basics/structure.html" class="menu-link">目录结构</a></li><li><a href="/zh-cn/basics/objects.html" class="menu-link">内置对象</a></li><li><a href="/zh-cn/basics/env.html" class="menu-link">运行环境</a></li><li><a href="/zh-cn/basics/config.html" class="menu-link">配置</a></li><li><a href="/zh-cn/basics/middleware.html" class="menu-link">中间件</a></li><li><a href="/zh-cn/basics/router.html" class="menu-link">Router</a></li><li><a href="/zh-cn/basics/controller.html" class="menu-link">Controller</a></li><li><a href="/zh-cn/basics/service.html" class="menu-link">Service</a></li><li><a href="/zh-cn/basics/plugin.html" class="menu-link">插件</a></li><li><a href="/zh-cn/basics/schedule.html" class="menu-link">定时任务</a></li><li><a href="/zh-cn/basics/extend.html" class="menu-link">框架扩展</a></li><li><a href="/zh-cn/basics/app-start.html" class="menu-link">启动自定义</a></li></ul></dd><dt id="title-Core" style="cursor: pointer;" class="aside-title">核心功能<a id="collapse-icon-Core" class="icon opend"></a></dt><dd id=panel-Core><ul><li><a href="/zh-cn/core/development.html" class="menu-link">本地开发</a></li><li><a href="/zh-cn/core/unittest.html" class="menu-link">单元测试</a></li><li><a href="/zh-cn/core/deployment.html" class="menu-link">应用部署</a></li><li><a href="/zh-cn/core/logger.html" class="menu-link">日志</a></li><li><a href="/zh-cn/core/httpclient.html" class="menu-link">HttpClient</a></li><li><a href="/zh-cn/core/cookie-and-session.html" class="menu-link">Cookie and Session</a></li><li><a href="/zh-cn/core/cluster-and-ipc.html" class="menu-link">多进程模型和进程间通讯</a></li><li><a href="/zh-cn/core/view.html" class="menu-link">模板渲染</a></li><li><a href="/zh-cn/core/error-handling.html" class="menu-link">异常处理</a></li><li><a href="/zh-cn/core/security.html" class="menu-link">安全</a></li><li><a href="/zh-cn/core/i18n.html" class="menu-link">国际化</a></li></ul></dd><dt id="title-Tutorials" style="cursor: pointer;" class="aside-title">教程<a id="collapse-icon-Tutorials" class="icon opend"></a></dt><dd id=panel-Tutorials><ul><li><a href="/zh-cn/tutorials/mysql.html" class="menu-link">MySQL</a></li><li><a href="/zh-cn/tutorials/restful.html" class="menu-link">RESTful API</a></li><li><a href="/zh-cn/tutorials/passport.html" class="menu-link">Passport 鉴权</a></li><li><a href="/zh-cn/tutorials/socketio.html" class="menu-link">Socket.IO</a></li><li><a href="/zh-cn/tutorials/assets.html" class="menu-link">静态资源</a></li><li><a href="/zh-cn/tutorials/typescript.html" class="menu-link">TypeScript</a></li></ul></dd><dt id="title-Advanced" style="cursor: pointer;" class="aside-title">进阶<a id="collapse-icon-Advanced" class="icon opend"></a></dt><dd id=panel-Advanced><ul><li><a href="/zh-cn/advanced/loader.html" class="menu-link">Loader</a></li><li><a href="/zh-cn/advanced/plugin.html" class="menu-link">插件开发</a></li><li><a href="/zh-cn/advanced/framework.html" class="menu-link">框架开发</a></li><li><a href="/zh-cn/advanced/cluster-client.html" class="menu-link">多进程研发模式增强</a></li><li><a href="/zh-cn/advanced/view-plugin.html" class="menu-link">模板插件开发规范</a></li><li><a href="/zh-cn/style-guide.html" class="menu-link">代码风格指南</a></li></ul></dd><dt id="title-Community" style="cursor: pointer;" class="aside-title">社区<a id="collapse-icon-Community" class="icon opend"></a></dt><dd id=panel-Community><ul><li><a href="/zh-cn/plugins/" class="menu-link">内置插件列表</a></li><li><a href="/zh-cn/contributing.html" class="menu-link">如何贡献</a></li><li><a href="/zh-cn/resource.html" class="menu-link">资源</a></li><li><a href="/zh-cn/faq.html" class="menu-link">常见问题</a></li></ul></dd></dl>
</aside>
<script>
var mobileTrigger = document.getElementById('mobileTrigger');
var mobileAside = document.getElementById('mobileAside');

var expandMenu = function(title) {
  // handle icon
  const collapseIcon = document.getElementById('collapse-icon-' + title);
  if (collapseIcon) {
    collapseIcon.className = 'icon opend';
  }
  // handle panelEle
  const panelEle = document.getElementById('panel-' + title);
  if (panelEle) {
    panelEle.className = '';
  }
}

var collapseMenu = function(title) {
  // handle icon
  const collapseIcon = document.getElementById('collapse-icon-' + title);
  if (collapseIcon) {
    collapseIcon.className = 'icon closed';
  }
  // handle panelEle
  const panelEle = document.getElementById('panel-' + title);
  if (panelEle) {
    panelEle.className = 'aside-panel-hidden';
  }
}

mobileAside.onclick = function(e) {
  const targetId = e.target.id;
  if (targetId && (targetId.indexOf('title-') > -1 || targetId.indexOf('collapse-icon-') > -1)) {
    const title = targetId.replace('title-', '').replace('collapse-icon-', '');
    try { 
      // the the browser may have no localStroage or JSON.parse may throw exception.
      const menuInfo = JSON.parse(window.localStorage.getItem('menuInfo'));
        
      // current menu status
      const curClosed = menuInfo[title] ? menuInfo[title].closed : false; // default false

      // change UI
      curClosed ? expandMenu(title) : collapseMenu(title);

      // save menuInfo to localStorage
      menuInfo[title] = { closed: !curClosed } // opposite
      window.localStorage.setItem('menuInfo', JSON.stringify(menuInfo));
    } catch (e) {}
  }
};

mobileTrigger.onclick = function(e) {
  e.preventDefault();
  if (mobileAside.className.indexOf('mobile-show') === -1) {
    mobileAside.className += ' mobile-show';
  } else {
    mobileAside.className = 'toc';
  }
};

(function() {
  // save data to localStorage because the page will refresh when user change the url.
  let menuInfo;
  try { 
    // the the browser may have no localStroage or JSON.parse may throw exception.
    menuInfo = JSON.parse(window.localStorage.getItem('menuInfo'));
    if (!menuInfo) {
      menuInfo = {};
      window.localStorage.setItem('menuInfo', JSON.stringify(menuInfo));
    }
  } catch (e) {
    menuInfo = {}; // default {}
  }

  for (const title in menuInfo) {
    if (menuInfo[title] && menuInfo[title].closed) { // menu in closed status.
      collapseMenu(title);
    } else {
      expandMenu(title);
    }
  }

  // highlight menu
  const pathname = window.location.pathname;
  const selector = `a[href="${pathname}"].menu-link,a[href="${pathname}index.html"].menu-link`;
  const menuItem = mobileAside.querySelector(selector);
  if (menuItem) { menuItem.className += ' highlight'; }
})();
</script>

</div>

  </div>
</body>
<script src="https://cdn.jsdelivr.net/docsearch.js/2/docsearch.min.js"></script>
<script>
docsearch({
  apiKey: '1561de31a86f79507ea00cdb54ce647c',
  indexName: 'eggjs',
  inputSelector: '#search-query',
});
</script>
<div class="cnzz">
<script src="https://s11.cnzz.com/z_stat.php?id=1261142226&web_id=1261142226" language="JavaScript"></script>
</div>

</html>
